概述
我们在自定义 View 控件时随处可见 Matrix 的身影,主要用于坐标转换映射,我们可以通过 Matrix 矩阵来控制视图的变换。
Matrix 本质上是一个如下图所示的矩阵:
上面每个值都有其对应的操作。
Matrix 提供了如下几个操作:
缩放(Scale)
对应 MSCALE_X 与 MSCALE_Y位移(Translate)
对应 MTRANS_X 与 MTRANS_Y错切(Skew)
对应 MSKEW_X 与 MSKEW_Y旋转(Rotate)
旋转没有专门的数值来计算,Matrix 会通过计算缩放与错切来处理旋转。最后三个参数是控制透视的,这三个参数主要在3D效果中运用,通常为(0, 0, 1),不在本篇讨论范围内,暂不过多叙述
示意
假设通知栏高度为20像素,导航栏高度为40像素,那么我们在内容区的(0,0)位置绘制一个点,最终就要转化为在实际坐标系中的(0,60)位置绘制一个点。
原理
矩阵乘法规则还记得吗?
我们在使用 Matrix 处理视图变换时本质上是通过矩阵映射坐标。
所以上述的几个操作都是对矩阵的操作,我们新建一个 Matrix 后其矩阵为默认状态,其值如下:
可以看到默认状态下的数据都是初始值,即不做任何变换处理,所有坐标保持原样
缩放
对于单个坐标来说,缩放只要将其坐标值值乘以缩放值即可。
假设对某个点宽度缩放 k1 倍,高度缩放 k2 倍,该点坐标为 x0、y0,缩放后坐标为 x、y,那么缩放的公式如下:
用矩阵来描述一下(上面提到了,与缩放相关的两个位置是 MSCALE_X 与 MSCALE_Y,即下图k1,k2的位置)
等号左边的矩阵就是计算后的缩放结果。
Matrix 中用于缩放操作的方法有如下两个:
1 | void setScale(float sx, float sy); |
前面两个参数 sx、sy,分别是宽和高的缩放比例。
第二个重载方法多了两个参数 px、py,这两个参数用来描述缩放的枢轴点
枢轴点是指定转换应保持不变的坐标。
当我们不传这两个参数时,枢轴点默认为左上角的点,缩放都是向下和向右,所以枢轴点可以大概的理解为缩放的锚点,缩放从这个点开始向四周扩散。
通过矩阵来理解一下:
1 | Matrix matrix = new Matrix() |
缩放 0.5 倍,枢轴点为 300,调用该方法后矩阵变换为:
前面提到过,上图右上角两个150值对应的位置是 MTRANS_X 与 MTRANS_Y,即和平移操作相关的两个位置。因此,实际上我们设置了枢轴点后 Matrix 会做一次位移操作,平移距离就是 s * p.
位移
位移操作是指将坐标(x0,y0)平移一定的距离,我们直接将坐标加上平移的距离即可得到平移后的坐标:
通过矩阵理解(上面提到了,与平移相关的两个位置是 MTRANS_X 与 MTRANS_Y,即下图△x,△y的位置):
用于设置位移操作的方法:
1 | void setTranslate(float dx, float dy); |
错切
看张图感受一下:
上图是通过下面的代码设置错切的示意图
1 | matrix.setSkew(0.3F, 0.3F); |
分别设置了水平错切、垂直错切的值为 0.3,效果就是上面的样子。
错切公式如下:
通过矩阵理解(上面提到了,与错切相关的两个位置是 MSKEW_X 与 MSKEW_Y,即下图k1,k2的位置):
错切操作方法(和前面缩放一样)
1 | void setSkew(float kx, float ky); |
旋转
旋转公式:
矩阵:
设置旋转的方法:
1 | void setRotate(float degrees); |
Matrix复合变换
复合变换是指矩阵同时实现两种或以上变换,例如在平移的同时改变其大小。
Matrix 的复合变换实际上就是矩阵相乘,原理很简单,但是因为矩阵相乘不符合交换律、且执行顺序对结果会有影响,所以想准确的使用好符合变换需要了解其原理。
上面我们在介绍这几种变换的同时也说了他们对应的方法,可以看到他们都是 set 方法,但 Matrix 中实际上提供了三种操作,分别是:设置(set)、前乘(pre)以及后乘(post)。
所以上述介绍的几个 set 方法都有与之对应的 pre 及 post 方法,方法列表如下:
1 | //scale 缩放 |
这三种的区别:
设置(set):如果我们不需要考虑复合变换的情况,一般可以直接使用 set 方法,因为 set 方法可能会重置之前的 Matrix 状态,导致之前设置的变换失效。
前乘(pre):
前乘相当于矩阵右乘:
我们假设当前矩阵 M 为:
用pre方法做一次平移操作
1 | matrix.preTranslate(100, 100); |
变换过程如下:(可以很明显的看到,当前是右乘)
后乘
后乘相当于矩阵左乘:
还是用刚才上面那个矩阵,同样对其做一次平移操作,使用post
1
matrix.postTranslate(100, 100);
变换过程如下:(可以看到,是左乘)